<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>
<body>
  <div id="mountNode"></div>
</body>
<script src="./assets/dagre.js"></script>
<script src="../build/g6.js"></script>
<script>
  /**
   * node 特殊属性
   */
  const nodeExtraAttrs = {
    begin: {
      fill: "#9FD4FB"
    },
    end: {
      fill: "#C2E999"
    }
  };

  const data = {
    nodes: [
      {
        id: "1",
        label: "请求回放1（开始）",
        type: "begin"
      },
      {
        id: "2",
        label: "交易创建"
      },
      {
        id: "3",
        label: "请求回放3"
      },
      {
        id: "4",
        label: "请求回放4"
      },
      {
        id: "5",
        label: "请求回放5"
      },
      {
        id: "6",
        label: "请求回放6"
      },
      {
        id: "7",
        label: "请求回放2（结束）",
        type: "end"
      }
    ],
    edges: [
      {
        source: "1",
        target: "2"
      },
      {
        source: "1",
        target: "3"
      },
      {
        source: "2",
        target: "5"
      },
      {
        source: "5",
        target: "6"
      },
      {
        source: "6",
        target: "7"
      },
      {
        source: "3",
        target: "4"
      },
      {
        source: "4",
        target: "7"
      }
    ]
  };

  /**
   * 自定义节点
   */
  G6.registerNode(
    "node",
    {
      drawShape: function drawShape(cfg, group) {
        var rect = group.addShape("rect", {
          attrs: {
            x: -75,
            y: -25,
            width: 150,
            height: 50,
            radius: 4,
            fill: "#FFD591",
            fillOpacity: 1,
            ...nodeExtraAttrs[cfg.type]
          }
        });
        return rect;
      },
      // 设置状态
      setState(name, value, item) {
        const group = item.getContainer();
        const shape = group.get("children")[0]; // 顺序根据 draw 时确定

        if (name === "selected") {
          if (value) {
            shape.attr("fill", "#F6C277");
          } else {
            shape.attr("fill", "#FFD591");
          }
        }
      },
      getAnchorPoints: function getAnchorPoints() {
        return [[0, 0.5], [1, 0.5]];
      }
    },
    "single-shape"
  );

  /**
   * 自定义 edge 中心关系节点
   */
  G6.registerNode(
    "statusNode",
    {
      drawShape: function drawShape(cfg, group) {
        const circle = group.addShape("circle", {
          attrs: {
            x: 0,
            y: 0,
            r: 6,
            // fill: '#ccc',
            fill: cfg.active ? "#AB83E4" : "#ccc"
          }
        });
        // const text = group.addShape('text', {
        //   attrs: {
        //    x: 0,
        //    y: -20,
        //    textAlign: 'center',
        //    text: cfg.label,
        //    fill: '#444'
        //   }
        // });
        return circle;
      }
    },
    "single-shape"
  );

  /**
 * 自定义带箭头的贝塞尔曲线 edge
 */
G6.registerEdge("line-with-arrow", {
  itemType: "edge",
  draw: function draw(cfg, group) {
    const startPoint = cfg.startPoint;
    const endPoint = cfg.endPoint;
    const centerPoint = {
      x: (startPoint.x + endPoint.x) / 2,
      y: (startPoint.y + endPoint.y) / 2
    };
    // 控制点坐标
    const controlPoint = {
      x: (startPoint.x + centerPoint.x) / 2,
      y: startPoint.y
    };

    console.log(cfg, startPoint, endPoint);

    // 为了更好的展示效果, 对称贝塞尔曲线需要连到箭头根部
    const path = group.addShape("path", {
      attrs: {
        path: [
          ["M", startPoint.x, startPoint.y],
          [
            "Q",
            controlPoint.x + 8,
            controlPoint.y,
            centerPoint.x,
            centerPoint.y
          ],
          ["T", endPoint.x - 8, endPoint.y],
          ["L", endPoint.x, endPoint.y]
        ],
        stroke: "#ccc",
        lineWidth: 1.6,
        endArrow: {
          path: "M 4,0 L -4,-4 L -4,4 Z",
          d: 4
        }
      }
    });

    // 计算贝塞尔曲线上的中心点
    // 参考资料 https://stackoverflow.com/questions/54216448/how-to-find-a-middle-point-of-a-beizer-curve
    // const lineCenterPoint = {
    //   x:
    //     (1 / 8) * startPoint.x +
    //     (3 / 8) * (controlPoint.x + 8) +
    //     (3 / 8) * centerPoint.x +
    //     (1 / 8) * (endPoint.x - 8),
    //   y:
    //     (1 / 8) * startPoint.y +
    //     (3 / 8) * controlPoint.y +
    //     (3 / 8) * centerPoint.y +
    //     (1 / 8) * endPoint.y
    // };

    // 在贝塞尔曲线中心点上添加圆形
    const { source, target } = cfg;
    group.addShape("circle", {
      attrs: {
        id: `statusNode${source}-${target}`,
        r: 6,
        x: centerPoint.x,
        y: centerPoint.y,
        fill: cfg.active ? "#AB83E4" : "#ccc"
      }
    });

    return path;
  }
});

  const g = new dagre.graphlib.Graph();
  g.setDefaultEdgeLabel(function() { return {}; });
  g.setGraph({ rankdir: 'LR' });

  data.nodes.forEach(node => {
    g.setNode(node.id + '', { width: 150, height: 50 });
  });

  data.edges.forEach(edge => {
    edge.source = edge.source + '';
    edge.target = edge.target + '';
    g.setEdge(edge.source, edge.target);
  });
  dagre.layout(g);

  let coord;
  g.nodes().forEach((node, i) => {
    coord = g.node(node);
    data.nodes[i].x = coord.x;
    data.nodes[i].y = coord.y;
  });
  g.edges().forEach((edge, i) => {
    coord = g.edge(edge);
    const startPoint = coord.points[0];
    const endPoint = coord.points[coord.points.length - 1];
    data.edges[i].startPoint = startPoint;
    data.edges[i].endPoint = endPoint;
    data.edges[i].controlPoints = coord.points.slice(
      1,
      coord.points.length - 1
    );
  });

  const graph = new G6.Graph({
    container: "mountNode",
    width: 1000,
    height: 800,
    defaultNode: {
      shape: "node",
      labelCfg: {
        style: {
          fill: "#fff",
          fontSize: 14
        }
      }
    },
    defaultEdge: {
      shape: "line-with-arrow"
    },
    edgeStyle: {
      default: {
        endArrow: true,
        lineWidth: 2,
        stroke: "#ccc"
      }
    }
  });

  graph.data(data);
  graph.render();

  graph.on('edge:click', evt => {
    const { target } = evt
    const type = target.get('type')
    if(type === 'circle') {
      // 点击边上的circle
      alert('你点击的是边上的圆点')
    }
  })

</script>
</html>